package com.example.mywike.config.shiroConfig;

import com.example.mywike.user.entity.RoleEntity;
import com.example.mywike.user.entity.UserLoginEntity;
import com.example.mywike.user.mapper.MenuMapper;
import com.example.mywike.user.mapper.RoleMapper;
import com.example.mywike.user.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;

@Slf4j
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private MenuMapper menuMapper;


    /**
     * @param credentialsMatcher 用户输入信息
     * @author xiyangqilvgui
     * @decription 设置凭证匹配器(与用户添加操作使用相同的加密算法)
     * @date 2020/9/7 0:04
     * @version 1.0.0
     * */
    @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        //1.构建CredentialsMatcher对象
        HashedCredentialsMatcher hcm=new HashedCredentialsMatcher();
        //2.设置加密算法
        hcm.setHashAlgorithmName(ShiroConfigStingInteger.ALGORITHM_MD5);
        //3.设置加密次数
        hcm.setHashIterations(ShiroConfigStingInteger.TIMES_1);
        super.setCredentialsMatcher(hcm);
    }

    /**
     * @param token 用户输入信息
     * @author xiyangqilvgui
     * @decription  重写shiro认证方法
     * @date 2020/9/7 0:04
     * @version 1.0.0
     * @return AuthenticationInfo
     * */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //1.获取用户名(用户页面输入)
        UsernamePasswordToken upToken= (UsernamePasswordToken)token;
        String identifier=upToken.getUsername();
        String host = upToken.getHost();

        String[] param = identifier.split(ShiroConfigStingInteger.DELIMITER);

        //2.基于用户名查询用户信息
        UserLoginEntity user = userMapper.findUserByUserName(param[0],param[1]);
        //3.判定用户是否存在
        if(user==null) {
            throw new UnknownAccountException();
        }

        //4.判定用户是否已被禁用。
        if(user.getEnabled()==0) {
            throw new LockedAccountException();
        }

        //5.封装用户信息
        ByteSource credentialsSalt=ByteSource.Util.bytes(user.getIdentifier());

        //记住：构建什么对象要看方法的返回值
        SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(
                        user,//principal (身份)
                        user.getCredential(),//hashedCredentials
                        credentialsSalt, //credentialsSalt
                        getName());//realName
        //6.返回封装结果
        //返回值会传递给认证管理器(后续认证管理器会通过此信息完成认证操作)
        log.info("[--- authentication successful ---]");
        return info;
    }




    /**
     * @param principal
     * @author xiyangqilvgui
     * @decription 为用户授权
     * @date 2020/9/13 21:10
     * @version 1.0.0
     * @return
     * */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        //获取session中的用户
        UserLoginEntity user=(UserLoginEntity) principal.fromRealm(this.getClass().getName()).iterator().next();
        log.info("user -> {}" ,user);
        UserLoginEntity user1 = (UserLoginEntity)principal.getPrimaryPrincipal();
        log.info("user1 -> {}",user1);

        // 根据用户id查询用户角色
        user1.setRoles(roleMapper.findRoleByUserId(user1.getUserId()));
        if (Objects.isNull(user1.getRoles()) || user1.getRoles().isEmpty()){
            throw new AuthorizationException();
        }
        // 根据用户角色，查询用户权限信息
        user1.setMenus(menuMapper.findMenuByRoles(user1.getRoles()));
        if (Objects.isNull(user1.getMenus()) || user1.getMenus().isEmpty()){
            throw new AuthorizationException();
        }

        List<String> permissions=new ArrayList<>();
        user1.getMenus().forEach(menu->permissions.add(menu.getPath()));

        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
        //将权限放入shiro中.
        info.addStringPermissions(permissions);
        return info;
    }
}